package cn.crawler.app.crown.modules.services.impl.pl;

import cn.crawler.app.crown.commons.util.DateUtil;
import cn.crawler.app.crown.commons.websocket.action.PdfAction;
import cn.crawler.app.crown.modules.models.CrownBet;
import cn.crawler.app.crown.modules.models.CrownConfig;
import cn.crawler.app.crown.modules.models.CrownMatch;
import cn.crawler.app.crown.modules.models.constant.CrownConstant;
import cn.crawler.app.crown.modules.models.constant.CrownPattern;
import cn.crawler.app.crown.modules.models.enums.BetsEnum;
import cn.crawler.app.crown.modules.services.CrownBetService;
import cn.crawler.app.crown.modules.services.CrownConfigService;
import cn.crawler.app.crown.modules.services.CrownMatchService;
import cn.crawler.app.crown.modules.services.pl.PdHalfResolverService;
import cn.crawler.app.sys.modules.services.SysParamService;
import cn.crawler.framework.base.service.BaseServiceImpl;
import cn.crawler.framework.util.Result;
import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.dubbo.config.annotation.Service;
import com.alibaba.fastjson.JSONArray;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.dao.Chain;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.integration.jedis.RedisService;
import org.nutz.ioc.aop.Aop;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.Strings;
import org.nutz.log.Log;
import org.nutz.log.Logs;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;

/**
 * 半场波胆玩法爬取
 */
@IocBean(args = {"refer:dao"})
@Service(interfaceClass = PdHalfResolverService.class)
public class PdHalfResolverServiceImpl extends BaseServiceImpl<CrownMatch> implements PdHalfResolverService {

    public PdHalfResolverServiceImpl(Dao dao) {
        super(dao);
    }

    private static final Log log = Logs.get();

    @Inject
    private RedisService redisService;
    @Inject
    private CrownMatchService matchService;
    @Inject
    private CrownBetService betService;
    @Inject
    @Reference(retries = 0, timeout = 500000)
    private CrownConfigService crownConfigService;
    @Reference
    @Inject
    private SysParamService sysParamService;

    /**
     * 投资内容和对应的赔率index
     */
    private Map<String, Integer> betMap = Result.createHashMap(
            "1-0", 8, "2-0", 9, "2-1", 10, "3-0", 11, "3-1", 12, "3-2", 13, "0-0", 18, "1-1", 19, "2-2", 20,
            "3-3", 21, "other", 23, "0-1", 24, "0-2", 25, "1-2", 26, "0-3", 27, "1-3", 28, "2-3", 29
    );


    public void crawler(String dateType, Integer pageNo, CrownConfig config) {
        new PageAction() {
            @Override
            public void push(List<JSONArray> list, String date) {
                log.info("hpd 准备发送数据====");
                PdfAction.instance().push("pagination", Result.success()
                        .addData(Result.createMap("date", date,
                                "list", list, "dateType", dateType)));
                log.info("hpd send====" + list.toString());
                list.forEach(gm -> {
                    CrownMatch match = new CrownMatch();
                    // 赛事id
                    match.setMid(gm.getString(0));
                    // 开赛时间
                    String[] dateTime = gm.getString(1).toLowerCase().split("<br>");
                    int dateTimeLength = dateTime.length;
                    // 爬取赛事id
                    match.setMid(gm.getString(0));
                    match.setStart(dateTimeLength == 3 ? dateTime[2] : "");
                    if (CrownConstant.FUTURE.equals(dateType)) {
                        match.setDateTime(DateUtil.parseDate(date.substring(0, 5) + dateTime[0] + " " + (dateTimeLength >= 2 ? dateTime[1] + "m" : ""), DateUtil.DATE_FORMAT_PATTERN.Y_M_D_H_M_A.PATTERN));
                    } else {
                        match.setDateTime(DateUtil.parseDate(date + " " + (dateTimeLength >= 2 ? dateTime[1] + "m" : ""), DateUtil.DATE_FORMAT_PATTERN.Y_M_D_H_M_A.PATTERN));
                    }
                    // 赛事阶段
                    match.setStage(dateType);
                    // 联赛名
                    match.setLeague(gm.getString(2));
                    // 主队名(去掉‘[Mid]’)
                    String home = gm.getString(5).trim();
                    home = CrownPattern.HTML_PATTERN.matcher(home).replaceAll("");
                    //中场判断
                    if (home.contains("[Mid]")) {
                        home = home.replace("[Mid]", "").trim();
                        match.setHaveHomeCourt(false);
                    } else {
                        match.setHaveHomeCourt(true);
                    }
                    match.setHome(home);
                    // 是否有滚球
                    if (Strings.isNotBlank(match.getStart()) && match.getStart().contains("running ball")) {
                        match.setHaveRoll(true);
                    } else {
                        match.setHaveRoll(false);
                    }
                    // 客队名(要去掉HTML标签)
                    String custom = gm.getString(6).trim();
                    custom = CrownPattern.HTML_PATTERN.matcher(custom).replaceAll("");
                    match.setCustom(custom);
                    // 查询赛事是否已入库
                    CrownMatch old = matchService.queryByTeamInfo(match.getLeague(), match.getHome(), match.getCustom(), match.getDateTime());
                    if (old != null) {
                        match.setId(old.getId());
                        log.info("update赛事,主:" + match.getHome() + ".客:" + match.getCustom() + ".联赛:" + match.getLeague()
                                + ".时间:" + match.getDateTime() + ".mid:" + match.getMid() + ".任务:半场波胆");
                        matchService.update(match);
                        this.updateBets(gm, match.getId());
                    }
                    // 插入
                    else {
                        log.info("首次insert赛事,主:" + match.getHome() + ".客:" + match.getCustom() + ".联赛:" + match.getLeague()
                                + ".时间:" + match.getDateTime() + ".mid:" + match.getMid() + ".任务:半场波胆");
                        matchService.insert(match);
                        this.insertBets(gm, match.getId());
                    }
                });
            }

            @Aop(TransAop.REPEATABLE_READ)
            private void updateBets(JSONArray gm, String matchId) {
                int count = betService.count(Cnd.where("matchId", "=", matchId)
                        .and("type", "=", BetsEnum.HALF_PD));
                // 更新
                if (count > 0) {
                    betMap.forEach((key, value) -> {
                        // 只更新rate字段(赔率)
                        Chain chain = Chain.make("rate", gm.getBigDecimal(value));
                        Cnd cnd = Cnd.where("matchId", "=", matchId)
                                .and("type", "=", BetsEnum.HALF_PD)
                                .and("betContent", "=", key);
                        betService.update(chain, cnd);
                    });
                }
                // 插入
                else {
                    this.insertBets(gm, matchId);
                }
            }

            private void insertBets(JSONArray gm, String matchId) {
                List<CrownBet> bets = new ArrayList<>();
                // 获取‘招募金额’
                BigDecimal recruitAmount = BigDecimal.ZERO;
                // 获取投资内容以及赔率
                betMap.forEach((key, value) -> {
                    CrownBet bet = new CrownBet();
                    bet.setMatchId(matchId);
                    bet.setType(BetsEnum.HALF_PD);
                    bet.setRecruitAmount(recruitAmount);
                    // 记录波胆比分
                    bet.setBetContent(key);
                    // 获取赔率
                    bet.setRate(gm.getBigDecimal(value));
                    // 加入集合
                    bets.add(bet);
                });
                betService.fastInsert(bets);
            }

        }.page(dateType, pageNo, config);
    }

    @Override
    public void browse(CrownConfig crownConfig) {
        crawler(CrownConstant.BROWSE, 0, crownConfig);
    }

    @Override
    public void future(CrownConfig crownConfig) {
        crawler(CrownConstant.FUTURE, 0, crownConfig);
    }


    /**
     * 分页获取数据
     */
    public abstract class PageAction {

        // 处理每一页数据，只需要组装返回
        public abstract void push(List<JSONArray> list, String date);

        void page(String type, Integer pageNo, CrownConfig config) {

            while (true) {
                log.info("准备请求爬取半场波胆玩法实时数据");
                // 早盘不传gdate则是获取明天的赛事
                String tailUrl = String.format("/app/member/FT_%s/body_var.php?uid=%s&rtype=hpd&langx=%s&mtype=3" +
                                "&page_no=%d&league_id=&hot_game=&isie11=%%27N%%27",
                        type, config.getUid(), CrownConstant.LANGX, pageNo);
                // 发送请求，获取响应body
                String body = crownConfigService.getCrownData(tailUrl, config, CrownConstant.RE_TRY_TIMES);
                // 检测账号被登出
                if (crownConfigService.checkLogout(body, config)) {
//                    // 重试登录(包括重试三次、自动请求新地址)
//                    crownConfigService.loginHg(config);
//                    // 如果还是登录失败 直接return结束 避免死循环
//                    if (config.isLogout()) {
//                        return;
//                    }
//                    // 登录成功则continue重新去请求
//                    continue;
                    return;
                }
                // body是空，监测到皇冠维护都return
                if (Strings.isBlank(body)) {
                    log.error("爬取半场波胆失败，body为空");
                    return;
                } else if (crownConfigService.checkMaintain(body, config)) {
                    return;
                }
                // 解析数据
                // 页数
                Matcher m = CrownPattern.TOTAL_PAGE_NUM_PATTERN.matcher(body);
                Integer pg = 0;
                if (m.find()) {
                    pg = Integer.valueOf(m.group(0).replace(";", "").replace("t_page=", ""));
                    log.info("获取总页数：" + pg);
                } else {
                    log.error("爬取不到页数,即将开启维护.URL:" + tailUrl + "===body:" + body);
                    // 如果爬取不到总页数,调用监测接口
                    crownConfigService.checkMaintain(CrownConstant.MAINTAIN_INFO_SHORT, config);
                }
                m = CrownPattern.SYSTEM_DATE_PATTERN.matcher(body);
                String date = null;
                if (m.find()) {
                    date = m.group(0).replace("top.today_gmt = '", "").replace("';", "");
                    log.info("获取系统日期：" + date);
                }
                // body_var 列表信息
                m = CrownPattern.G_METHOD_DATA_PATTERN.matcher(body);
                List<JSONArray> list = new ArrayList<>();
                while (m.find()) {
                    JSONArray gm;
                    String v = m.group();
                    v = v.substring(2, v.length() - 2);
                    gm = JSONArray.parseArray(v);
                    for (int i = 8; i < 34; i++) {
                        if (Strings.isEmpty(gm.getString(i))) {
                            gm.remove(i);
                            gm.add(i, 0);
                        }
                    }
                    list.add(gm);
                }
                if (pg <= ++pageNo) {
                    log.info("爬取成功，当前（half_pd）页面数据条数half_pd->pageNo:" + pageNo + ",pageSize:" + list.size());
                    push(list, date);
                    return;
                }
            }
        }
    }
}
